import { BaseService } from '@/core/base.service';
import { CardAirdropDto, CardListDto, CardQueryDto } from '@/dtos';
import { halt } from '@/utils';
import { Injectable } from '@nestjs/common';
import { Card, Prisma } from '@prisma/client';

@Injectable()
export class CardService extends BaseService {
  async list({ params, sort }: CardListDto, userId: number) {
    const { current = 1, pageSize = 20, keyword, ...where } = params;

    let sql = `
            SELECT #{fields} FROM Card AS t
            WHERE t.isDeleted = false 
            ${!!where.title ? `AND t.title LIKE '%${where.title}%'` : ''}
            ${!!where.collectionId ? `AND t.collectionId = ${where.collectionId}` : ''}
        `;

    const [{ count }] = await this.prisma.$queryRawUnsafe<[{ count: number }]>(
      sql.replace('#{fields}', 'COUNT(t.id) AS count'),
    );

    const list = await this.prisma.$queryRawUnsafe<any[]>(
      `${sql.replace(
        '#{fields}',
        `
            t.id, t.no, t.title, t.price, t.image, t.collectionId,
            (
              SELECT 
                COUNT(c.cardId) 
              FROM UserHoldCard AS c
              WHERE c.cardId = t.id
                AND
                    c.userId = ${userId}
            ) AS hold
        `,
      )}
            ORDER BY ${
              !!sort && Object.keys(sort).length > 0
                ? Object.entries(sort)
                    .map(
                      ([k, v]) => `t.${k} ${v === 'descend' ? 'DESC' : 'ASC'}`,
                    )
                    .join(',')
                : 't.id ASC'
            }
            LIMIT ${pageSize} 
            OFFSET ${(current - 1) * pageSize}
        `,
    );

    const total = count ? Number(count) : 0;
    const totalPage = Math.ceil(total / pageSize);
    const hasMore = current < totalPage;

    return {
      total,
      totalPage,
      hasMore,
      current,
      pageSize,
      list: list.map((o) => ({
        ...o,
        price: Number(o.price),
        hold: Number(o.hold),
      })),
    };
  }

  async query({ params, sort }: CardQueryDto) {
    const { current = 1, pageSize = 20, keyword, ...where } = params;

    let sql = `
            SELECT #{fields} FROM Card AS t
            WHERE t.isDeleted = false 
            ${!!where.title ? `AND t.title LIKE '%${where.title}%'` : ''}
            ${!!where.collectionId ? `AND t.collectionId = ${where.collectionId}` : ''}
        `;

    const [{ count }] = await this.prisma.$queryRawUnsafe<[{ count: number }]>(
      sql.replace('#{fields}', 'COUNT(t.id) AS count'),
    );

    const list = await this.prisma.$queryRawUnsafe<Card[]>(
      `${sql.replace(
        '#{fields}',
        `
            t.id, t.no, t.title, t.price, t.image, t.collectionId
        `,
      )}
            ORDER BY ${
              !!sort && Object.keys(sort).length > 0
                ? Object.entries(sort)
                    .map(
                      ([k, v]) => `t.${k} ${v === 'descend' ? 'DESC' : 'ASC'}`,
                    )
                    .join(',')
                : 't.id DESC'
            }
            LIMIT ${pageSize} 
            OFFSET ${(current - 1) * pageSize}
        `,
    );

    const total = count ? Number(count) : 0;
    const totalPage = Math.ceil(total / pageSize);
    const hasMore = current < totalPage;

    return {
      total,
      totalPage,
      hasMore,
      current,
      pageSize,
      list: list.map((o) => ({
        ...o,
        price: Number(o.price),
      })),
    };
  }

  async findOne(
    where: Prisma.CardWhereUniqueInput,
    select?: Prisma.CardSelect,
  ): Promise<Card | null> {
    return this.prisma.card.findUnique({
      where,
      select,
    });
  }

  async create(data: Prisma.CardCreateInput): Promise<Card> {
    return await this.prisma.card.create({
      data: {
        ...data,
      },
    });
  }

  async update(params: {
    where: Prisma.CardWhereUniqueInput;
    data: Prisma.CardUpdateInput;
  }): Promise<Card> {
    const { where, data } = params;
    return this.prisma.card.update({
      where,
      data: {
        ...data,
      },
    });
  }

  async remove(where: Prisma.CardWhereUniqueInput): Promise<Card> {
    return this.prisma.card.update({
      where,
      data: {
        isDeleted: true,
      },
    });
  }

  async airdrop({ id, rows }: CardAirdropDto) {
    return await this.redlock.using(
      [`card:airdrop:${id}`],
      100000,
      {},
      async () => {
        for (let r of rows) {
          await this.prisma.$transaction(async (tx) => {
            const card = await tx.card.findUnique({
              where: {
                id,
              },
            });
            if (!card) {
              return halt('卡牌不存在');
            }
            const user = await tx.user.findUnique({
              where: {
                mobile: r.mobile,
              },
            });
            if (!!user) {
              for (let i = 0; i < Number(r.num); i++) {
                await tx.userHoldCard.create({
                  data: {
                    cardId: card.id,
                    userId: user.id,
                  },
                });
              }
              console.log(`空投《${card.title}》卡牌 ${r.mobile} - ${r.num}`);
            } else {
              console.log(`用户未注册: ${r.mobile}`);
            }
          });
        }
      },
    );
  }
}
